package com.thinkingcoder.tcconfig.server.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import com.thinkingcoder.tcconfig.server.DistributedLocks;
import com.thinkingcoder.tcconfig.server.dal.ConfigsMapper;
import com.thinkingcoder.tcconfig.server.model.dbo.Configs;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
public class TcConfigController {

    @Autowired
    DistributedLocks locks;

    @Autowired
    ConfigsMapper configsMapper;

    Map<String, Long> VERSIONS = new HashMap<>();

    private final MultiValueMap<String, DeferredResult<Long>> DEFERRED_VERSION = new LinkedMultiValueMap<>();

    @GetMapping("/list")
    public List<Configs> list(String app, String env, String ns) {
        return configsMapper.list(app, env, ns);
    }

    @RequestMapping("/update")
    public List<Configs> update(@RequestParam("app") String app, @RequestParam("env") String env,
        @RequestParam("ns") String ns, @RequestBody Map<String, String> params) {
        params.forEach((k, v) -> {
            insertOrUpdate(new Configs(app, env, ns, k, v));
        });
        String key = String.join("-", app, env, ns);
        VERSIONS.put(key, System.currentTimeMillis());
        DEFERRED_VERSION.getOrDefault(key, new ArrayList<>()).forEach(dr -> dr.setResult(VERSIONS.get(key)));
        return configsMapper.list(app, env, ns);
    }

    private void insertOrUpdate(Configs configs) {
        Configs conf = configsMapper.select(configs.getApp(), configs.getEnv(), configs.getNs(), configs.getPkey());
        if (conf == null) {
            configsMapper.insert(configs);
        } else {
            configsMapper.update(configs);
        }
    }

    @GetMapping("/version")
    public long version(String app, String env, String ns) {
        return VERSIONS.getOrDefault(app + "-" + env + "-" + ns, -1L);
    }

    @GetMapping("/deferred/version/{app}/{env}/{ns}/{version}")
    public DeferredResult<Long> deferredVersion(@PathVariable String app, @PathVariable String env,
        @PathVariable String ns, @PathVariable Long version) {

        long currentVersion = version(app, env, ns);

        String key = String.join("-", app, env, ns);

        log.info("deferred version ===> key : {} , clientVersion : {} , serverVersion : {}", key, version,
            currentVersion);

        DeferredResult<Long> result;
        if (currentVersion != version) {
            result = new DeferredResult<>();
            result.setResult(currentVersion);
            return result;
        }

        result = new DeferredResult<>(10_000L, version);
        result.onCompletion(() -> {
            System.out.println("==>>>[[[completion]]]<<<===");
            DEFERRED_VERSION.remove(key);
        });
        result.onTimeout(() -> {
            System.out.println("==>>>[[[timeout]]]<<<===");
            result.setResult(currentVersion);
            DEFERRED_VERSION.remove(key);
        });
        DEFERRED_VERSION.add(key, result);
        log.info("deferred version ===> return defer for {}", key);
        return result;

    }

    @GetMapping
    public boolean status() {
        return locks.getLocked().get();
    }

}
